<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Пуля. Рейтинг великих самарских преферансистов 2015</title>

	<style type="text/css">
		table.results {
			border-collapse: collapse;
		}
		table.results th {
			font-weight: normal;
			color: #878069;
			background-color: #F8F4E7;
			border: 1px solid #DDDDDD;
			padding: 5px;
		}
/*
		table.competitionsList td {
			padding: 5px;
			border: 1px solid #DDDDDD;
		}
*/
		table.results td {
			padding: 0 5px;
			border: 1px solid #DDDDDD;
			text-align: right;
		}
		table.results td.center {
			text-align: center;
		}
		table.results td.left {
			text-align: left;
		}

		table.results tr.odd {
			background-color: #F7F6EB;
		}
		table.results tr.even {
		}

		table.results td.positivePoints {
			color: #2F8415;
		}
		table.results td.negativePoints {
			color: #FF0000;
		}

		table.results td.leftSeparate, table.results th.leftSeparate {
			border-left: 2px solid #DDDDDD;
		}
		table.results td.rightSeparate, table.results th.rightSeparate {
			border-right: 2px solid #DDDDDD;
		}
		table.results td.topSeparate, table.results th.topSeparate {
			border-top: 2px solid #DDDDDD;
		}

		table.results tr.summary td {
			border-top: 2px solid #DDDDDD;
		}

		h3 {
			color: #878069;
			margin-bottom: 5px;
			margin-top: 20px;
		}

		.attent {
			color: #FF0000;
		}
	</style>
</head>
<body>

<h3>Общая таблица сыгранных пуль</h3>
<div id="total-table-container"></div>

<h3>Рейтинг в порядке очков<sup class="attent">*</sup>, <sup class="attent">**</sup></h3>
<div id="pointsRating-table-container"></div>
<sup class="attent">*</sup> &mdash; количество очков в&nbsp;зависимости от&nbsp;модуля вистов:
от&nbsp;0&nbsp;до&nbsp;10&nbsp;&mdash;&nbsp;0;
от&nbsp;11&nbsp;до&nbsp;50&nbsp;&mdash;&nbsp;1;
от&nbsp;51&nbsp;до&nbsp;150&nbsp;&mdash;&nbsp;2;
от&nbsp;151&nbsp;до&nbsp;300&nbsp;&mdash;&nbsp;3;
от&nbsp;301&nbsp;до&nbsp;500&nbsp;&mdash;&nbsp;4;
от&nbsp;501&nbsp;и&nbsp;больше&nbsp;&mdash;&nbsp;5
<br/>
<sup class="attent">**</sup> &mdash; сумма очков может быть не&nbsp;равна&nbsp;0, т.к.&nbsp;изменение очков за&nbsp;висты идет не&nbsp;через одинаковые интервалы.
Пример: +348&nbsp;вистов&nbsp;(+4&nbsp;очка), &minus;82&nbsp;виста&nbsp;(&minus;2&nbsp;очка), &minus;266&nbsp;вистов&nbsp;(&minus;3&nbsp;очка).
В&nbsp;этом случае сумма вистов равна (348&nbsp;&minus;&nbsp;82&nbsp;&minus;&nbsp;266)&nbsp;=&nbsp;0,
а&nbsp;сумма очков равна (4&nbsp;&minus;&nbsp;2&nbsp;&minus;&nbsp;3)&nbsp;=&nbsp;&minus;1&nbsp;<>&nbsp;0.

<h3>Рейтинг в порядке вистов</h3>
<div id="whistsRating-table-container"></div>


<script type="text/javascript">
	var PLAYERS = { BEZUS: 'Bezus', POPOV: 'Popov', SHAMAEV: 'Shamaev', SERGEEV: 'Sergeev', FEDOSEEV: 'Fedoseev', VELIKANOVA: 'Velikanova' };

	// todo: think whether ids are needed
	var players = [
		  { id: 1, name: 'Безус&nbsp;Евгений&nbsp;Анатольевич', shortName: 'Е. Б.', mnemonic: PLAYERS.BEZUS }
		, { id: 2, name: 'Попов&nbsp;Дмитрий&nbsp;Андреевич', shortName: 'Д. П.', mnemonic: PLAYERS.POPOV }
		, { id: 3, name: 'Шамаев&nbsp;Михаил&nbsp;Игоревич', shortName: 'М. Ш.', mnemonic: PLAYERS.SHAMAEV }
		, { id: 4, name: 'Сергеев&nbsp;Александр&nbsp;Владиславович', shortName: 'А. С.', mnemonic: PLAYERS.SERGEEV }
		, { id: 5, name: 'Федосеев&nbsp;Виктор&nbsp;Андреевич', shortName: 'В. Ф.', mnemonic: PLAYERS.FEDOSEEV }
		, { id: 6, name: 'Великанова&nbsp;Татьяна&nbsp;Валерьевна', shortName: 'Т. В.', mnemonic: PLAYERS.VELIKANOVA }
	];

	// js object (kind of JSON) in data
	// pool - пуля. @see https://en.wikipedia.org/wiki/Preferans#Scoring_system
	var pools = [
		  {
			  date: '32.13.2014'
//			, fileName: 'Пока не отсканен'
			, comment: 'Неизвестная пуля, лежавшая у Миши. Видимо, от 2014 года.'
//			, exclude: true
			, results: [
				  { player: PLAYERS.SHAMAEV, whists: 396 }
				, { player: PLAYERS.BEZUS, whists: -55 }
				, { player: PLAYERS.SERGEEV, whists: -341 }
			]
		}
		, {
			  date: '02.01.2015'
			, fileName: 'pref_2015_01_04.pdf'
			, comment: 'Алик взял 8 на девятерной на чужом ходе (убитка)'
			, results: [
				  { player: PLAYERS.POPOV, whists: 588 }
				, { player: PLAYERS.BEZUS, whists: 9 }
				, { player: PLAYERS.SERGEEV, whists: -597 }
			]
		}
		, {
			  date: '04.01.2015'
			, fileName: 'pref_2015_01_04.pdf'
			, comment: 'Решающий шестерной распас (Женя 0, Дима 5, Миша 5)'
			, results: [
				  { player: PLAYERS.BEZUS, whists: 348 }
				, { player: PLAYERS.POPOV, whists: -82 }
				, { player: PLAYERS.SHAMAEV, whists: -266 }
			]
		}
		, {
			  date: '08.01.2015'
			, fileName: 'pref_2015_01_08.pdf'
			, results: [
				  { player: PLAYERS.SERGEEV, whists: 132 }
				, { player: PLAYERS.POPOV, whists: 80 }
				, { player: PLAYERS.BEZUS, whists: -212 }
			]
		}
		, {
			  date: '18.01.2015'
			, fileName: 'pref_2015_01_18.pdf'
			, comment: 'Несчастливый расклад шестерного распаса (Женя 0, Миша 4, Дима 6). Даже записан у меня в блокноте. В целом - нехарактерно малые модули результатов для Ленинградки.'
			, results: [
				  { player: PLAYERS.SHAMAEV, whists: 98 }
				, { player: PLAYERS.SERGEEV, whists: 78 }
				, { player: PLAYERS.BEZUS, whists: -22 }
				, { player: PLAYERS.POPOV, whists: -154 }
			]
		}
		, {
			  date: '25.01.2015'
			, fileName: 'pref_2015_01_25.pdf'
			, comment: 'И снова плохо сыгранный шестерной распас для Димы (7 взяток). А Таня пришла и сыграла очень сильно.'
			, results: [
				  { player: PLAYERS.BEZUS, whists: 336 }
				, { player: PLAYERS.VELIKANOVA, whists: 276 }
				, { player: PLAYERS.POPOV, whists: -202 }
				, { player: PLAYERS.SHAMAEV, whists: -410 }
			]
		}
		, {
			  date: '15.02.2015'
			, fileName: 'pref_2015_02_15.pdf'
			, comment: 'Миша был несколько нездоров и взял 9 на шестерных распасах, а потом еще две на мизере. А потом выпил жаропонижающее и стал играть гораздо лучше. В целом пуля прошла под знаком нескончаемых распасов: Дима, например, не сыграл ни одной игры, кроме двух вынужденных семерных без одной. Зато выход из распасов пришелся ровно на назначенное время окончания - 22:30'
			, results: [
				  { player: PLAYERS.BEZUS, whists: 612 }
				, { player: PLAYERS.POPOV, whists: -8 }
				, { player: PLAYERS.SHAMAEV, whists: -604 }
			]
		}
		, {
			  date: '22.02.2015'
//			, fileName: 'pref_2015_02_22.pdf'
			, comment: 'Дима взял 1 на мизере и был без 2 на шестерной втемную (козыри 4 в 0 и не угадал с ходом в некозырного туза, которого убили козырем). Витя взял 6 на первых распасах и 6 на вынужденной семерной.'
			, results: [
				  { player: PLAYERS.BEZUS, whists: 164 }
				, { player: PLAYERS.FEDOSEEV, whists: -19 }
				, { player: PLAYERS.POPOV, whists: -145 }
			]
		}
		, {
			  date: '15.03.2015'
//			, fileName: 'pref_2015_03_15.pdf'
			, comment: 'Алик был два раза подряд чистым на шестых распасах (Женя и Дима взяли в них 7-3 и 3-7) и в последней сдаче сыграл мизер, прикупив 7 к ТК8 и получив после сноса расклад с плотными младшими картами во всех мастях. Женя заказал гениальную шестерную, ошибочно заказав козырем бубну, в которой у него было Т107, 7 в сносе (и взял при этом целых 5 взяток! При правильном козыре было бы 8&nbsp;взяток). В пуле из соображения сохранения целочисленности вистов убраны трети.'
			, results: [
				  { player: PLAYERS.SERGEEV, whists: 1062 }
				, { player: PLAYERS.POPOV, whists: -297 }
				, { player: PLAYERS.BEZUS, whists: -765 }
	]
		}
		, {
			  date: '22.03.2015'
//			, fileName: 'pref_2015_03_22.pdf'
			, comment: 'Женя напрасно завистовал девятерную, где Дима застраховался на третью козырную даму, а ее не было.&#10; Миша сел на первой сдаче без одной на шестерной и сыграл первую игру в пулю — семерную — в последней сдаче.&#10; Дима два раза был чистым на первых распасах: первый раз Миша на последние три взятки зачехлил Женю, а не Диму, а второй раз расклад был крайне благоприятным (пронос трех карт на первых трех ходах). Также на очередной паленой шестерной Диме предолжили 6 взяток там, где было 5 из-за неправильного сноса.&#10; До третьих распасов дело ни разу не дошло. &#10;&#10; Миша взял одну на паленом мизере, и не факт, что там был одна, а не 2.&#10; Расклад:&#10;Миша W: П: 7 9 В, Т: 7 10 Д К, Б: 7 10 К, Ч: 8 9. Снос: бубны 10 К&#10;Женя E: П: ДК, Т: 9 В Т, Б: 8 9, Ч: 7 В Т&#10;Дима S: П: 8 10 Т, Т: 8, Б: В Д Т, Ч: 10 Д К. &#10;Ход Жени. Отдали одну взятку в трефе. Вроде как должна быть еще взятка в&nbsp;черве (снос двух черв с руки Димы на две трефы)'
			, results: [
				  { player: PLAYERS.POPOV, whists: 568 }
				, { player: PLAYERS.BEZUS, whists: -42 }
				, { player: PLAYERS.SHAMAEV, whists: -526 }
			]
		}
		, {
			  date: '29.03.2015'
//			, fileName: 'pref_2015_03_29.pdf'
			, comment: 'Женя два раза подряд был чистым на шестых распасах, на которых Дима набрал 6 и 7 взяток, надежно обеспечивших итоговый проигрыш. Также Женя сыграл шестерную, на которой было пять взяток, из-за неверного хода вистующих (не&nbsp;прорезали козырь, и козырь был выбит ногой из-под некозырного марьяжа).&#10; Алик мастерски сыграл паленый мизер, оставив от мастей П: 7 8, Т: 7 8 10 В Д Т, Б: 9 B и Ч: 8 Т черву (для перехвата как защиты от паровоза) + неправильный ход вистующих — зашли сразу в бубну, тогда как надо было проверить, будет ли играющий перехватывать черву. Однако потом Алик с необъяснимым оптимизмом заказал 8 и взял 7. Также Миша ошибся с ходом на двух вистах на игре Алика, где было 7 как 6, а получилась своя игра.'
			, results: [
				  { player: PLAYERS.BEZUS, whists: 493 }
				, { player: PLAYERS.SHAMAEV, whists: 233 }
				, { player: PLAYERS.SERGEEV, whists: -249 }
				, { player: PLAYERS.POPOV, whists: -477 }
			]
		}
		, {
			  date: '12.04.2015'
//			, fileName: 'pref_2015_04_12.pdf'
			, comment: 'Пуля прошла под девизами «Множественные ошибки» и «Что делать при трех тузах в состоянии непервых распасов?». Женя не сыграл ни одной игры (единственный возможный раз переторгован Димой), Дима и Таня садились на шестерных и семерных. На распасах, на удивление, больше 4&nbsp;взяток никто не взял ни разу. Явные ошибки:&#10; Дима сыграл семерную без одной, при угрозе на вторых распасах не более, чем 5 взяток. &#10; Дима неверно заказал козырь на своем ходе на шестерной: Ч: КДВ7, Б: ТКВ10, назначена козырем черва, а по раскладу бубна в пополаме, а черва 4 к 0. &#10; Коллективно выпустили шестерую Тани из без одной в свою игру, ради передачи хода для убитки несчитанных (!) взяток наиграв взятку на второго короля. &#10; Таня на своем ходе на семерной вышла не в козырного туза, а в К от некозырного марьяжа пик с ногой. Король был принят тузом, и оставшиеся две пики убиты. &#10; Миша непонятно зачем вернул вист на восьмерной игре Тани, при отсутствии козырей на своей руке (!), втором пасе Жени (!!) и отсутствии явных взяток в трех некозырных мастях (!!!). В итоге Таня взяла по раскладу 10 взяток.'
			, results: [
				  { player: PLAYERS.SHAMAEV, whists: 131 }
				, { player: PLAYERS.VELIKANOVA, whists: 11 }
				, { player: PLAYERS.POPOV, whists: -57 }
				, { player: PLAYERS.BEZUS, whists: -85 }
			]
		}
		, {
			  date: '19.04.2015'
//			, fileName: 'pref_2015_04_19.pdf'
			, comment: 'Алик взял строгий курс на побитие рекорда максимального проигрыша: сначала сыграв две семерные за счет равного расклада в двух мастях (!), затем: сел на семерной без одной, на шестерной без двух, и два раза взяв на паленых мизерах по 3. &#10; Дима совершил грубую ошибку, на третьих распасах пронеся отыгранную масть вместо оставшейся берущей на руке; в итоге 3 взятки вместо 0. Также Дима два раза сыграл раскладные игры (вынужденная семерная и шестерная на трехструнке) и один раз сел на семерной без одной, страхуясь от безнадежных вторых распасов.&#10; Женя на адекватном мизере на первой руке не прикупил и взял одну свою взятку.'
			, results: [
				  { player: PLAYERS.POPOV, whists: 644 }
				, { player: PLAYERS.BEZUS, whists: 512 }
				, { player: PLAYERS.SERGEEV, whists: -1156 }
			]
		}
		, {
			  date: '26.04.2015'
//			, fileName: 'pref_2015_04_26.pdf'
			, comment: 'Первая часть пули прошла под девизом &laquo;распасы забанены&raquo;: сплошняком шли игры, в том числе крупные, а единожды попытавшиеся было зародиться распасы были уже на втором круге перебиты семерной. Однако, ближе к исходу основного времени пули распасы все-таки взяли свое, и продожались до конца времени пули, потребовав даже немного компенсированного. При этом выход из распасов произошел вынужденной семерной Вити, на которой Женя и Дима ушли за полвиста, хотя по раскладу оказалось, что могло не быть и 7 взяток.  &#10; До входа в распасы прикупы были почти постоянно благоприятными для играющих. &#10; До входа в шестерные распасы Дима уверенно лидировал, сыграв два чистых с прикупом мизера, 10 БК и девятерную, в итоге доведя пулю до 52. Однако на третьих распасах он взял два раза по 6 взяток, при этом грубо ошибившись на одних из них, где при правильной игре было бы 2-3 взятки (!). Только последовавшие одними из последних удачные распасы (Дима 1, Витя 4, Женя 5) со скрежетом вернули Диме первое место в пуле, но уже с минимальным отрывом от Жени.  &#10; Женя играл аккуратно, минимально залезая в гору и нередко заказывая игры с чрезмерной страховкой (благодаря чему Дима завистовал 7&nbsp;как&nbsp;9, а Витя — 7&nbsp;как&nbsp;9 и 8&nbsp;как&nbsp;10).  &#10; Витя сыграл мизер, бывший чистым со сдачи, но пара уходов без трех, подсады на висте и на игре (в том числе шестерная, сыгранная без двух!) обеспечили третье место, хотя и с далеко не самым большим минусом. &#10; По ходу одной из раздач был рожден остроумный мем &laquo;комплекс прикупной рыбы&raquo;: на столе перед Витей лежала тарелка в форме рыбы, выделенная для кожурок от семечек. На одной из сдач Вити почти до последнего круга карт он не клал прикуп, и, когда ему напомнили об этом, аргументировал: «Да мне рыба вот эта мешает!», что было тут же подхвачено как шутка о том, что фраза подходит для случая приема преферансиста у психоаналитика и поставленного в итоге диагноза «комплекс прикупной рыбы».'
			, results: [
				  { player: PLAYERS.POPOV, whists: 203 }
				, { player: PLAYERS.BEZUS, whists: 169 }
				, { player: PLAYERS.FEDOSEEV, whists: -372 }
			]
		}
		, {
			  date: '10.05.2015'
//			, fileName: 'pref_2015_05_10.pdf'
			, comment: 'Пуля характеризовалась большим числом перекошенных раскладов на распасах (часто один из игроков и/или сдатчик были чистыми, несколько раз встречался пронос после ходов из прикупа, а один раз даже пронос двумя игроками после выхода из прикупа), и большим числом сыгранных крупных игр (много восьмерных и девятерных, и всего один подсад на шестерной). &#10; В начальной фазе пули одной сдачей шестерных распасов были расставлены места, сохранившиеся далее — Миша взял 0, Алик и Женя — по 5, Дима — 0&nbsp;на&nbsp;прикупе.  &#10; В последней по времени сдаче был выполнен стратегический заход в распасы, но&nbsp;продолжались оные недолго, буквально пару сдач. К тому же распасы на&nbsp;последней сдаче никак не повлияли на распределение мест — Миша был чистым на прикупе, Дима взял 2, Алик и Женя — по 4. &#10; Также была сыграна нехарактерная серия из трех подряд шестерных с&nbsp;обоснованным вистованием и итогом 6 как 6. &#10; Миша надежно захватил лидерство с самого начала, удачно играя распасы (в том числе вышел чистым на вторых и третьих распасах). Получить максимум очков за&nbsp;+500 вистов ему помешало, в первую очередь, необоснованно рискованное вистование на восьмерной Жени, где некозырная масть лежала пополам, и&nbsp;получилось 8 как 10. &#10; Дима в начале пули один раз не угадал с выбором между семерной игрой и пасом на вторых распасах, получив 6 взяток на распасах. Затем Дима сыграл почти подряд два обоснованно рискованных раскладных мизера: один — с дыркой 7 10 и&nbsp;второй — с&nbsp;7 9 Д при выходе со своего хода в бланковую 8. &#10; Женя несколько раз набирал немало взяток на раскладных распасах, а потом перестраховался на своем ходе от расклада 4 к 0, сыграв 8 как 10. &#10; Алик неоптимально играл распасы, один раз ушел 7 без 3 и один раз сел без&nbsp;одной на&nbsp;шестерной, но зато сыграл пару рискованных крупных игр (8 и 9) с&nbsp;чужого хода.'
			, results: [
				  { player: PLAYERS.SHAMAEV, whists: 456 }
				, { player: PLAYERS.POPOV, whists: 310 }
				, { player: PLAYERS.BEZUS, whists: -196 }
				, { player: PLAYERS.SERGEEV, whists: -570 }
			]
		}
		, {
			  date: '14.06.2015'
//			, fileName: 'pref_2015_06_14.pdf'
			, comment: ' Пуля подтвердила гамблеровскую статистику о недолгих сериях распасов &mdash; заходов в распасы было достаточно много, но ни одних третьих распасов так&nbsp;и&nbsp;не&nbsp;сыграли. Также получила наглядное подтверждение поговорка &laquo;Пуля — дура, вист — молодец&raquo; — результаты пули во&nbsp;многом были определены балансом вистов. &#10; Женя сыграл всего три игры, но с самого начала уверенно захватил лидерство на&nbsp;вторых распасах (Женя 0, Миша 3, Дима 7), и далее отлично играл распасы (самый серьезный минус — 6 взяток на первых распасах) и четко вистуя, записав в&nbsp;итоге на всех плюсовой баланс по вистам (включая даже 24&nbsp;виста на&nbsp;единственной игре Димы). Отдельно стоит отметить грамотный второй вист Жени на посаженной семерной Алика, где Женя взял 3, а Миша 1. &#10; Миша достаточно хорошо играл распасы и верно заказывал и заторговывал игры (был лишь один подсад на&nbsp;шестерной без&nbsp;одной), но минусовой баланс по&nbsp;вистам привел к итоговому отставанию от Жени, несмотря на&nbsp;значительно большую пулю у&nbsp;Миши. &#10; Дима вначале неверно завистовал семерную Миши, а затем дважды ошибся в&nbsp;стадии вторых распасов: один раз не вступив в торговлю, и в итоге 7 взяток (а&nbsp;с&nbsp;прикупом было бы верных 7 на игре), и второй раз вместо 1 взятки взяв 5 (Женя на&nbsp;первых ходах пронес 9 треф, а Дима через несколько ходов зашел в&nbsp;трефу, на что Женя пронес бубну, которая была единственным отходом Димы). Также Диме категорически не шла карта: два редких раза с пришедшей игрой были переторгованы (правда, один раз неуспешным мизером), и единственная семерная была сыграна уже под самый конец игры. От значительного проигрыша спасло только массовое заваливание вистами Миши и Алика. &#10; Алик сыграл много крупных игр, правда, два раза присев на семерных (одна из&nbsp;которых была вынужденной в распасах). Однако чрезмерно рискованный мизер (из&nbsp;П:&nbsp;7&nbsp;9&nbsp;T, Б:&nbsp;8&nbsp;9&nbsp;T вместо перехвата тузом оставлены 8 9 бубен в&nbsp;надежде на&nbsp;0&nbsp;взяток при&nbsp;удачном раскладе, в&nbsp;итоге — паровоз из&nbsp;четырех взяток) и восемь взяток на четверных распасах привели к итоговому приличному минусу (хотя рекорд проигрыша, поставленный Аликом же с помощью двух несыгранных мизеров, побит не был).'
			, results: [
				  { player: PLAYERS.BEZUS, whists: 400 }
				, { player: PLAYERS.SHAMAEV, whists: 354 }
				, { player: PLAYERS.POPOV, whists: -56 }
				, { player: PLAYERS.SERGEEV, whists: -698 }
			]
		}
	];

	var POOL_CASES = { singleNominative: 'пуля', singleGenitive: 'пули', multipleGenitive: 'пуль' };
	var SCAN_CASES = { singleNominative: 'скан', singleGenitive: 'скана', multipleGenitive: 'сканов' };

	function showError(message) {
		alert('Error: ' + message);
		throw message;
	}

	function findPlayer(mnemonic) {
		for (var i = 0; i < players.length; i++) {
			if (players[i].mnemonic == mnemonic)
				return players[i];
		}

		showError('player with mnemonic = "' + mnemonic + '" not found');
		return null;
	}
	function getPlayerResult(pool, playerMnemonic) {
		// todo: check pool and pool.results existence

		var results = pool.results;
		for (var i = 0; i < results.length; i++) {
			if (results[i].player == playerMnemonic)
				return results[i];
		}

		return null;
	}

	function getPlaceCellClass(place, pool) {
		if (place == 1) // todo: constant
			return ' positivePoints';
		else if (place >= pool.results.length)
			return ' negativePoints';
		else
			return ' zeroPoints';
	}
	function getPointsCellClass(points) {
		if (points < 0)
			return ' class="negativePoints"';
		else if (points > 0)
			return ' class="positivePoints"';
		else
			return ' class="zeroPoints"';
	}

	function getNounCase(count, cases) {
		var n = count % 100;
		if (n > 19)
			n %= 10;

		switch (n)
		{
			case 1:
				return cases.singleNominative;

			case 2:
			case 3:
			case 4:
				return cases.singleGenitive;

			default:
				return cases.multipleGenitive;
		}
	}
	function getPoolsNounCase(poolsCount) {
		return getNounCase(poolsCount, POOL_CASES);
	}

	function getScansCount(pools) {
		var scansCount = 0;

		for (var i = 0; i < pools.length; i++) {
			if (pools[i].fileName) // todo: use special hasScan file instead
				scansCount++;
		}

		return scansCount;
	}
	function getScansNounCase(poolsCount) {
		return getNounCase(poolsCount, SCAN_CASES);
	}

	function formatRealNumber(number) {
		return (number * 10 / 10).toFixed(2);
//		return (number * 1000 / 1000).toFixed(4);
	}

	function getFileDownloadLink(pool) {
		if (!pool.fileName)
			return '&nbsp;';

		return '<a href="/pref/' + pool.fileName + '">' + pool.fileName + '</a>'; // "/pref" is context path on "sergeyev.ru"
	}

	function validatePool(pool) {
		var date = pool.date;
		if (!date)
			showError('pool contains no date');

		var results = pool.results;
		if (!results)
			showError('pool from date "' + date + '" contains no results');

		// todo: check that results isArray

		if ( results.length != 3 && results.length != 4 ) // gusarik is not played :)
			showError('pool from date "' + date + '" contains incorrect number of players (' + results.length +'). Pool must contain 3 or 4 players.');

		var uniquePlayersMnemonics = [];
		var whistsSum = 0;
		var player;

		// validate players
		for (var i = 0; i < results.length; i++)
		{
			var result = results[i];

			player = result.player;
			if (!player)
				showError('pool from date "' + date + '": result[' + i + '] contains no player');

			if ( !findPlayer(player) )
				showError('pool from date "' + date + '": player "' + player + '" is unknown');

			// todo: check that all players are different
			if ( uniquePlayersMnemonics.indexOf(player) >= 0 )
				showError('pool from date "' + date + '": player "' + player + '" is present in results more than once');
			else
				uniquePlayersMnemonics.push(player);

			if ( !result.whists && result.whists !== 0 )
				showError('pool from date "' + date + '": result[' + i + '] (for player "' + player + '") contains no whists');

			// todo: check that whists is integer
			whistsSum += result.whists;
		}

		if (whistsSum != 0)
			showError('pool from date "' + date + '": whists sum (' + whistsSum + ') is incorrect. Whists sum of all players\' results must be equal to 0' );
	}

	function fillPoolPoints(pool) {
		var i;
		var results = pool.results;
		var playersCount = results.length;

		// todo: sort results by whists at the beginning
		// todo: solve equal whists situation

		// fill places: // todo: after sorting by whists
		for (i = 0; i < playersCount; i++)
			results[i].place = (i + 1);

		// place points
		switch (playersCount)
		{
			case 3:
				results[0].placePoints = 3;
				results[1].placePoints = 0;
				results[2].placePoints = -3;
				break;

			case 4:
				results[0].placePoints = 3;
				results[1].placePoints = 1;
				results[2].placePoints = -1;
				results[3].placePoints = -3;
				break;

			default:
				showError('Incorrect players count for pool: ' + playersCount);
		}

//			 0	от 0 до 10	 0
//			-1	от 11 до 50	+1
//			-2	от 51 до 150	+2
//			-3	от 151 до 300	+3
//			-4	от 301 до 500	+4
//			-5	от 501 и больше	+5
		var whistsPoints;
		var whists;
		var playerResult;

		for (i = 0; i < results.length; i++) {
			playerResult = results[i];
			whists = playerResult.whists;

			if (whists <= -501)
				whistsPoints = -5;
			else if (whists >= -500 && whists <= -301)
				whistsPoints = -4;
			else if (whists >= -300 && whists <= -151)
				whistsPoints = -3;
			else if (whists >= -150 && whists <= -51)
				whistsPoints = -2;
			else if (whists >= -50 && whists <= -11)
				whistsPoints = -1;
			else if (whists >= -10 && whists <= 10)
				whistsPoints = 0;
			else if (whists >= 11 && whists <= 50)
				whistsPoints = 1;
			else if (whists >= 51 && whists <= 150)
				whistsPoints = 2;
			else if (whists >= 151 && whists <= 300)
				whistsPoints = 3;
			else if (whists >= 301 && whists <= 500)
				whistsPoints = 4;
			else if (whists >= 501)
				whistsPoints = 5;
			else
				showError('Pool for date "' + pool.date + '": incorrect whists for player "' + player + '": ' + whists);

			playerResult.whistsPoints = whistsPoints;

			playerResult.totalPoints = playerResult.placePoints + playerResult.whistsPoints;
		}

		// todo: check places points sum is 0
		// ! whists points sum may be != 0 (because point grades are not equal)
	}

	function fillPlayerTotalResults(pools) {
		var i, j;
		var player, pool, playerResult, poolPlayersCount;

		for (i = 0; i < players.length; i++)
		{
			player = players[i];

			player.poolsPlayed = 0;
			player.positivePools = 0;
			player.negativePools = 0;

			player.placesSum = 0; // for counting average place in pool
			player.poolsWon = 0; // first place
			player.poolsLost = 0; // last place

			player.whists = 0;
			player.placePoints = 0;
			player.whistsPoints = 0;
			player.totalPoints = 0;

			player.maxPoolWhists = undefined;
			player.minPoolWhists = undefined;

			for (j = 0; j < pools.length; j++)
			{
				pool = pools[j];
				poolPlayersCount = pool.results.length;

				playerResult = getPlayerResult(pool, player.mnemonic);
				if (playerResult)
				{ // player played in the pool -> update player total results
					player.poolsPlayed++;

					if (playerResult.whists > 0)
						player.positivePools++;
					else if (playerResult.whists < 0)
						player.negativePools++;

					player.placesSum += playerResult.place;
					if (playerResult.place == 1) // todo: constant
						player.poolsWon++;
					else if ( playerResult.place == poolPlayersCount )
						player.poolsLost++;

					player.whists += playerResult.whists;
					player.placePoints += playerResult.placePoints;
					player.whistsPoints += playerResult.whistsPoints;
					player.totalPoints += playerResult.totalPoints;

					if ( (player.maxPoolWhists === undefined) || (playerResult.whists > player.maxPoolWhists) )
						player.maxPoolWhists = playerResult.whists;

					if ( (player.minPoolWhists === undefined) || (playerResult.whists < player.minPoolWhists) )
						player.minPoolWhists = playerResult.whists;
				}
			}
		}
	}

	function fillTotalTable(pools) {
		var container = document.getElementById('total-table-container');
		var i, j;
		var player;
		var playerResult;
		var pool;
		var rowClass;
		var cellClass;

		var html = '';
		html += '<table class="results"><tbody>';

		// headers rows
		html += '<tr>';

		html += '<th colspan="4">Пуля</th>';
		for (i = 0; i < players.length; i++)
		{
			player = players[i];
			html += '<th colspan="5" class="leftSeparate">' + player.name + '</th>';
		}

		html += '</tr>';

		html += '<tr>';
		html += '<th>Дата</th>';
		html += '<th>Файл скана</th>';
		html += '<th>Комментарий</th>';
		html += '<th title="3 или 4. Гусарик не играется :)">Игроков в&nbsp;пуле</th>';
		for (i = 0; i < players.length; i++)
		{
			html += '<th class="leftSeparate">Место</th>';
			html += '<th>Висты</th>';
			html += '<th>Очки за&nbsp;место</th>';
			html += '<th>Очки за&nbsp;висты</th>';
			html += '<th>Очки за&nbsp;пулю</th>';
		}

		html += '</tr>';

		// pools data
		for (i = 0; i < pools.length; i++)
		{
			rowClass = (i % 2) ? 'odd' : 'even';

			pool = pools[i];
			// todo: odd and even rows
			html += '<tr class="' + rowClass + '">';

			html += '<td class="center">' + pool.date + '</td>';

			html += '<td class="center">' + getFileDownloadLink(pool) + '</td>';

			if (pool.comment)
			{
				html += '<td class="center" title="' + pool.comment + '">Навести</td>';
			}
			else
			{
				html += '<td>&nbsp;</td>';
			}

			html += '<td>' + pool.results.length + '</td>';

			for (j = 0; j < players.length; j++)
			{
				player = players[j];

				playerResult = getPlayerResult(pool, player.mnemonic);
				if (!playerResult)
				{ // player did not take part in the pool
					html += '<td colspan="5" class="leftSeparate"></td>';
				}
				else
				{ // player took part in the pool
					html += '<td class="leftSeparate' + getPlaceCellClass(playerResult.place, pool) + '">' + playerResult.place + '</td>';
					html += '<td' + getPointsCellClass(playerResult.whists) + '>' + playerResult.whists + '</td>';
					html += '<td' + getPointsCellClass(playerResult.placePoints) + '>' + playerResult.placePoints + '</td>';
					html += '<td' + getPointsCellClass(playerResult.whistsPoints) + '>' + playerResult.whistsPoints + '</td>';
					html += '<td' + getPointsCellClass(playerResult.totalPoints) + '>' + playerResult.totalPoints + '</td>';
				}
			}

			html += '</tr>';
		}

		// summary row
		html += '<tr class="summary">';
		html += '<td>Итого</td>';
		html += '<td class="center">' + getScansCount(pools) + '&nbsp;' + getScansNounCase( getScansCount(pools) ) + '</td>';
		html += '<td class="center">' + pools.length + '&nbsp;' + getPoolsNounCase(pools.length) + '</td>';
		html += '<td class="center">' + '&nbsp;' + '</td>'; // todo: придумать, что здесь написать. Можно в среднем игроков в пуле, или число пуль на 3 и на 4 отдельно :)

		for (j = 0; j < players.length; j++)
		{
			player = players[j];

			html += '<td class="leftSeparate">' + player.poolsPlayed + '&nbsp;' + getPoolsNounCase(player.poolsPlayed) + '</td>';
			html += '<td' + getPointsCellClass(player.whists) + '>' + player.whists + '</td>';
			html += '<td' + getPointsCellClass(player.placePoints) + '>' + player.placePoints + '</td>';
			html += '<td' + getPointsCellClass(player.whistsPoints) + '>' + player.whistsPoints + '</td>';
			html += '<td' + getPointsCellClass(player.totalPoints) + '>' + player.totalPoints + '</td>';
		}

		html += '</tr>';

		html += '</tbody></table>';

		container.innerHTML = html;
	}

	function fillPointsRatingTable(pools) {
		// sort players by totalPoints desc
		players.sort(function(p1, p2) {
			if (p2.totalPoints == p1.totalPoints)
				return p2.whists - p1.whists; // in case of points equality, order is defined by total players' whists

			return p2.totalPoints - p1.totalPoints;
		});

		var container = document.getElementById('pointsRating-table-container');

		var html = '';

		html += '<table class="results"><tbody>';

		// header row
		html += '<tr>';
		html += '<th>Игрок</th>';
		html += '<th>Сыграно пуль</th>';
		html += '<th>Всего очков</th>';
		html += '<th>В&nbsp;среднем очков за&nbsp;пулю</th>';
		html += '<th>Всего вистов</th>';
		html += '<th>В&nbsp;среднем вистов за&nbsp;пулю</th>';
		html += '<th title="Максимальное число вистов, набранное за&nbsp;одну пулю">Максимальный выигрыш за&nbsp;пулю</th>';
		html += '<th title="Минимальное число вистов, набранное за&nbsp;одну пулю">Максимальный проигрыш за&nbsp;пулю</th>';
		html += '<th title="Первое место в&nbsp;пуле">Пуль выиграно</th>';
		html += '<th title="Последнее место в&nbsp;пуле">Пуль проиграно</th>';
		html += '<th title="Количество пуль с&nbsp;положительным вистовым результатом">Пуль в&nbsp;плюсе</th>';
		html += '<th title="Количество пуль с&nbsp;отрицательным вистовым результатом">Пуль в&nbsp;минусе</th>';
		html += '<th title="Показатель имеет сомнительную ценность в&nbsp;силу наличия пуль как&nbsp;на&nbsp;троих, так&nbsp;и&nbsp;на&nbsp;четверых. Добавлен для&nbsp;интереса.">Среднее место в&nbsp;пуле</th>';
		html += '</tr>';

		// players rows
		var player;
		var rowClass;
		for (var i = 0; i < players.length; i++)
		{
			rowClass = (i % 2) ? 'odd' : 'even';

			player = players[i];
			html += '<tr class="' + rowClass + '">';
			html += '<td class="left">' + player.name + '</td>';
			html += '<td>' + player.poolsPlayed + '</td>';
			html += '<td' + getPointsCellClass(player.totalPoints) + '>' + player.totalPoints + '</td>';
			html += '<td' + getPointsCellClass(player.totalPoints) + '>' + formatRealNumber(player.totalPoints / player.poolsPlayed ) + '</td>';
			html += '<td' + getPointsCellClass(player.whists) + '>' + player.whists + '</td>';
			html += '<td' + getPointsCellClass(player.whists) + '>' + formatRealNumber( player.whists / player.poolsPlayed) + '</td>';
			html += '<td' + getPointsCellClass(player.maxPoolWhists) + '>' + player.maxPoolWhists + '</td>';
			html += '<td' + getPointsCellClass(player.minPoolWhists) + '>' + player.minPoolWhists + '</td>';
			html += '<td>' + player.poolsWon + '</td>';
			html += '<td>' + player.poolsLost + '</td>';
			html += '<td>' + player.positivePools + '</td>';
			html += '<td>' + player.negativePools + '</td>';
			html += '<td>' + formatRealNumber( player.placesSum / player.poolsPlayed) + '</td>';
			html += '</tr>';
		}

		html += '</tbody></table>';

		container.innerHTML = html;
	}
	function fillWhistsRatingTable(pools) {
		// sort players by whists desc
		players.sort(function(p1, p2) {
			return p2.whists - p1.whists;
		});

		var container = document.getElementById('whistsRating-table-container');

		var html = '';

		html += '<table class="results"><tbody>';

		// header row
		html += '<tr>';
		html += '<th>Игрок</th>';
		html += '<th>Сыграно пуль</th>';
//		html += '<th>Всего очков</th>';
		html += '<th>Всего вистов</th>';
//		html += '<th title="Первое место в&nbsp;пуле">Пуль выиграно</th>';
//		html += '<th title="Последнее место в&nbsp;пуле">Пуль проиграно</th>';
//		html += '<th title="Количество пуль с&nbsp;положительным вистовым результатом">Пуль в&nbsp;плюсе</th>';
//		html += '<th title="Количество пуль с&nbsp;отрицательным вистовым результатом">Пуль в&nbsp;минусе</th>';
		html += '<th>В&nbsp;среднем вистов за&nbsp;пулю</th>';
		html += '</tr>';

		// players rows
		var player;
		var rowClass;
		for (var i = 0; i < players.length; i++)
		{
			rowClass = (i % 2) ? 'odd' : 'even';

			player = players[i];
			html += '<tr class="' + rowClass + '">';
			html += '<td class="left">' + player.name + '</td>';
			html += '<td>' + player.poolsPlayed + '</td>';
//			html += '<td' + getPointsCellClass(player.totalPoints) + '>' + player.totalPoints + '</td>';
			html += '<td' + getPointsCellClass(player.whists) + '>' + player.whists + '</td>';
//			html += '<td>' + player.poolsWon + '</td>';
//			html += '<td>' + player.poolsLost + '</td>';
//			html += '<td>' + player.positivePools + '</td>';
//			html += '<td>' + player.negativePools + '</td>';
			html += '<td' + getPointsCellClass(player.whists) + '>' + formatRealNumber(player.whists / player.poolsPlayed) + '</td>';
			html += '</tr>';
		}

		html += '</tbody></table>';

		container.innerHTML = html;
	}

	function processPools(pools) {
		// todo: possible "exclude: true" in pool
		for (var i = 0; i < pools.length; i++) { // todo: use forEach from ExtJs or jQuery
			var pool = pools[i];

			validatePool(pool);

			fillPoolPoints(pool);
		}

		fillPlayerTotalResults(pools);
		fillTotalTable(pools);
		fillPointsRatingTable(pools);
		fillWhistsRatingTable(pools);
	}

	processPools(pools)
</script>
</body>
</html>